﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.ObjectModel;
using DbSetupHelper.Plugin.ScriptPackage;
using System.Reflection;
using System.IO;
using DbSetupHelper.Common;
using System.Xml.XPath;

namespace DbSetupHelper.Business
{
    public class DatabaseController
    {
        /// <summary>
        /// Occurs when [on message].
        /// </summary>
        public event EventHandler<MessageEventArgs> OnMessage;
        private static DatabaseController instance = new DatabaseController();

        private DatabaseController()
        {
            DatabaseBuilder.OnMessage += new EventHandler<MessageEventArgs>(DatabaseBuilder_OnMessage);
        }

        void DatabaseBuilder_OnMessage(object sender, MessageEventArgs e)
        {
            if (OnMessage != null)
                OnMessage(this, e);
        }

        /// <summary>
        /// Runs the specified install parameters.
        /// </summary>
        /// <param name="installParameters">The install parameters.</param>
        public void Run(InstallParameters installParameters)
        {
            if (!installParameters.ExtractOnly)
            {
                DatabaseBuilder.Run(installParameters);
            }
            else
            {
                WriteScriptsToDisk(installParameters);
            }
        }

        /// <summary>
        /// Fetches the available db script packages.
        /// </summary>
        /// <returns></returns>
        /// <remarks>
        /// Script packages need to be in the currently executing directory.
        /// </remarks>
        public Collection<IScriptPackageMetadata> FetchAvailableDbScriptPackages()
        {
            Collection<IScriptPackageMetadata> returnList = new Collection<IScriptPackageMetadata>();

            string pluginDirectory = Assembly.GetExecutingAssembly().Location
                .Substring(0, Assembly.GetExecutingAssembly().Location.LastIndexOf('\\'));

            if (Directory.Exists(pluginDirectory))
            {
                foreach (string possiblePlugin in
                    Directory.GetFiles(pluginDirectory, "*.dll"))
                {
                    Assembly asm = Assembly.LoadFile(possiblePlugin);

                    foreach (Type t in asm.GetTypes())
                    {
                        if (t.GetInterface("IScriptPackageMetadata") != null)
                        {
                            //TODO: Figure out how to detect when there is a duplicate script set.
                            IScriptPackageMetadata metadata = Activator.CreateInstance(t) as IScriptPackageMetadata;
                            returnList.Add(metadata);
                        }
                    }
                }
            }
            return returnList;
        }

        /// <summary>
        /// Fetches the db scripts for package.
        /// </summary>
        /// <param name="packageFriendlyName">Name of the package friendly.</param>
        /// <returns></returns>
        public Collection<DBScript> FetchDbScriptsForPackage(string packageFriendlyName)
        {
            Collection<DBScript> scripts = null;

            foreach (Type t in FetchAssemblyForScriptPackage(packageFriendlyName).GetTypes())
            {
                if (t.GetInterface("IScriptPackage") != null)
                {
                    IScriptPackage metadata = Activator.CreateInstance(t) as IScriptPackage;
                    scripts = XmlParser.GenerateScriptPattern(metadata);
                }
            }
            return scripts;
        }

        /// <summary>
        /// Fetches the default run parameteres for package.
        /// </summary>
        /// <param name="packageFriendlyName">Friendly name of the package.</param>
        /// <returns></returns>
        public RunParameters FetchDefaultRunParameteresForPackage(string packageFriendlyName)
        {
            return FetchDefaultRunParameteresForPackage(FetchAssemblyForScriptPackage(packageFriendlyName));
        }

        /// <summary>
        /// Fetches the default run parameteres for package.
        /// </summary>
        /// <param name="assembly">The assembly.</param>
        /// <returns></returns>
        public RunParameters FetchDefaultRunParameteresForPackage(Assembly assembly)
        {
            RunParameters retValue = null;

            foreach (Type t in assembly.GetTypes())
            {
                if (t.GetInterface("IScriptPackage") != null)
                {
                    IScriptPackage data = Activator.CreateInstance(t) as IScriptPackage;
                    retValue = XmlParser.GenerateRunParms(data);
                }
            }

            return retValue;
        }

        /// <summary>
        /// Fetches the assembly for the script package.
        /// </summary>
        /// <param name="packageFriendlyName">The friendly name of the script package to search for.</param>
        /// <returns></returns>
        public Assembly FetchAssemblyForScriptPackage(string packageFriendlyName)
        {
            string pluginDirectory = Assembly.GetExecutingAssembly().Location
                .Substring(0, Assembly.GetExecutingAssembly().Location.LastIndexOf('\\'));

            if (Directory.Exists(pluginDirectory))
            {
                foreach (string possiblePlugin in
                    Directory.GetFiles(pluginDirectory, "*.dll"))
                {
                    Assembly asm = Assembly.LoadFile(possiblePlugin);
                    foreach (Type t in asm.GetTypes())
                    {
                        if (t.GetInterface("IScriptPackageMetadata") != null)
                        {
                            IScriptPackageMetadata metadata = Activator.CreateInstance(t) as IScriptPackageMetadata;
                            if (metadata.PackageFriendlyName == packageFriendlyName)
                                return asm;
                        }
                    }
                }
            }
            return null;
        }

        /// <summary>
        /// Writes the scripts to disk.
        /// </summary>
        /// <param name="installParameters">The install parameters.</param>
        /// <param name="path">The path.</param>
        private void WriteScriptsToDisk(InstallParameters installParameters)
        {
            if (!Directory.Exists(installParameters.ExtractDirectory))
                throw new DirectoryNotFoundException("path");
            if (installParameters == null)
                throw new ArgumentNullException("installParameters");

            TextWriter tw;

            foreach (DBScript script in installParameters.ScriptPattern)
            {
                //we only want to write .sql files.
                //keep the same file names and write them to the users folder choice.
                tw = new StreamWriter(installParameters.ExtractDirectory + "\\" + script.ScriptFileName);
                tw.Write(script.ScriptBody);

                //cleanup after ourselves. we like to make messes.
                tw.Close();
                tw.Dispose();
            }
        }

        /// <summary>
        /// Gets the singleton instance of the DatabaseController.
        /// </summary>
        /// <value>The instance.</value>
        public static DatabaseController Instance
        {
            get { return instance; }
        }        
    }
}
